/*
 * src/common/tutorial/complex.cpp
 *
 ******************************************************************************
  This file contains routines that can be bound to a openGauss backend and
  called by the backend in the process of processing queries.  The calling
  format for these routines is dictated by openGauss architecture.
 ******************************************************************************/

#include "postgres.h"
#include "knl/knl_variable.h"

#include "fmgr.h"
#include "libpq/pqformat.h" /* needed for send/recv functions */

PG_MODULE_MAGIC;

typedef struct Complex {
    double x;
    double y;
} Complex;

/*
 * Since we use V1 function calling convention, all these functions have
 * the same signature as far as C is concerned.  We provide these prototypes
 * just to forestall warnings when compiled with gcc -Wmissing-prototypes.
 */
Datum complex_in(PG_FUNCTION_ARGS);
Datum complex_out(PG_FUNCTION_ARGS);
Datum complex_recv(PG_FUNCTION_ARGS);
Datum complex_send(PG_FUNCTION_ARGS);
Datum complex_add(PG_FUNCTION_ARGS);
Datum complex_abs_lt(PG_FUNCTION_ARGS);
Datum complex_abs_le(PG_FUNCTION_ARGS);
Datum complex_abs_eq(PG_FUNCTION_ARGS);
Datum complex_abs_ge(PG_FUNCTION_ARGS);
Datum complex_abs_gt(PG_FUNCTION_ARGS);
Datum complex_abs_cmp(PG_FUNCTION_ARGS);

/*****************************************************************************
 * Input/Output functions
 *****************************************************************************/

PG_FUNCTION_INFO_V1(complex_in);

Datum complex_in(PG_FUNCTION_ARGS)
{
    char* str = PG_GETARG_CSTRING(0);
    double x, y;
    Complex* result = NULL;

    if (sscanf_s(str, " ( %lf , %lf )", &x, &y) != 2) {
        ereport(ERROR,
            (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid input syntax for complex: \"%s\"", str)));
    }

    result = (Complex*)palloc(sizeof(Complex));
    result->x = x;
    result->y = y;
    PG_RETURN_POINTER(result);
}

PG_FUNCTION_INFO_V1(complex_out);

Datum complex_out(PG_FUNCTION_ARGS)
{
    Complex* complex = (Complex*)PG_GETARG_POINTER(0);
    char* result = NULL;

    result = (char*)palloc(100);
    int rc = snprintf_s(result, 100, 99, "(%g,%g)", complex->x, complex->y);
    securec_check_ss(rc, "\0", "\0");
    PG_RETURN_CSTRING(result);
}

/*****************************************************************************
 * Binary Input/Output functions
 *
 * These are optional.
 *****************************************************************************/

PG_FUNCTION_INFO_V1(complex_recv);

Datum complex_recv(PG_FUNCTION_ARGS)
{
    StringInfo buf = (StringInfo)PG_GETARG_POINTER(0);
    Complex* result = NULL;

    result = (Complex*)palloc(sizeof(Complex));
    result->x = pq_getmsgfloat8(buf);
    result->y = pq_getmsgfloat8(buf);
    PG_RETURN_POINTER(result);
}

PG_FUNCTION_INFO_V1(complex_send);

Datum complex_send(PG_FUNCTION_ARGS)
{
    Complex* complex = (Complex*)PG_GETARG_POINTER(0);
    StringInfoData buf;

    pq_begintypsend(&buf);
    pq_sendfloat8(&buf, complex->x);
    pq_sendfloat8(&buf, complex->y);
    PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}

/*****************************************************************************
 * New Operators
 *
 * A practical Complex datatype would provide much more than this, of course.
 *****************************************************************************/

PG_FUNCTION_INFO_V1(complex_add);

Datum complex_add(PG_FUNCTION_ARGS)
{
    Complex* a = (Complex*)PG_GETARG_POINTER(0);
    Complex* b = (Complex*)PG_GETARG_POINTER(1);
    Complex* result = NULL;

    result = (Complex*)palloc(sizeof(Complex));
    result->x = a->x + b->x;
    result->y = a->y + b->y;
    PG_RETURN_POINTER(result);
}

/*****************************************************************************
 * Operator class for defining B-tree index
 *
 * It's essential that the comparison operators and support function for a
 * B-tree index opclass always agree on the relative ordering of any two
 * data values.  Experience has shown that it's depressingly easy to write
 * unintentionally inconsistent functions.	One way to reduce the odds of
 * making a mistake is to make all the functions simple wrappers around
 * an internal three-way-comparison function, as we do here.
 *****************************************************************************/

#define Mag(c) ((c)->x * (c)->x + (c)->y * (c)->y)

static int complex_abs_cmp_internal(Complex* a, Complex* b)
{
    double amag = Mag(a);
    double bmag = Mag(b);

    if (amag < bmag) {
        return -1;
    }
    if (amag > bmag) {
        return 1;
    }
    return 0;
}

PG_FUNCTION_INFO_V1(complex_abs_lt);

Datum complex_abs_lt(PG_FUNCTION_ARGS)
{
    Complex* a = (Complex*)PG_GETARG_POINTER(0);
    Complex* b = (Complex*)PG_GETARG_POINTER(1);

    PG_RETURN_BOOL(complex_abs_cmp_internal(a, b) < 0);
}

PG_FUNCTION_INFO_V1(complex_abs_le);

Datum complex_abs_le(PG_FUNCTION_ARGS)
{
    Complex* a = (Complex*)PG_GETARG_POINTER(0);
    Complex* b = (Complex*)PG_GETARG_POINTER(1);

    PG_RETURN_BOOL(complex_abs_cmp_internal(a, b) <= 0);
}

PG_FUNCTION_INFO_V1(complex_abs_eq);

Datum complex_abs_eq(PG_FUNCTION_ARGS)
{
    Complex* a = (Complex*)PG_GETARG_POINTER(0);
    Complex* b = (Complex*)PG_GETARG_POINTER(1);

    PG_RETURN_BOOL(complex_abs_cmp_internal(a, b) == 0);
}

PG_FUNCTION_INFO_V1(complex_abs_ge);

Datum complex_abs_ge(PG_FUNCTION_ARGS)
{
    Complex* a = (Complex*)PG_GETARG_POINTER(0);
    Complex* b = (Complex*)PG_GETARG_POINTER(1);

    PG_RETURN_BOOL(complex_abs_cmp_internal(a, b) >= 0);
}

PG_FUNCTION_INFO_V1(complex_abs_gt);

Datum complex_abs_gt(PG_FUNCTION_ARGS)
{
    Complex* a = (Complex*)PG_GETARG_POINTER(0);
    Complex* b = (Complex*)PG_GETARG_POINTER(1);

    PG_RETURN_BOOL(complex_abs_cmp_internal(a, b) > 0);
}

PG_FUNCTION_INFO_V1(complex_abs_cmp);

Datum complex_abs_cmp(PG_FUNCTION_ARGS)
{
    Complex* a = (Complex*)PG_GETARG_POINTER(0);
    Complex* b = (Complex*)PG_GETARG_POINTER(1);

    PG_RETURN_INT32(complex_abs_cmp_internal(a, b));
}
